package com.kaibes.orm.spring;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import com.kaibes.core.spring.ApplicationUtils;
import com.kaibes.orm.core.core.TableService;
import com.kaibes.orm.core.listener.DeleteListener;
import com.kaibes.orm.core.listener.InsertListener;
import com.kaibes.orm.core.listener.SelectListener;
import com.kaibes.orm.core.listener.UpdateListener;

@Component
public class OrmStartedListener implements ApplicationListener<ContextRefreshedEvent> {

    @Value("${kaibes.orm.use-cache:false}")
    private boolean useCache;
    @Autowired
    private ApplicationUtils applicationUtils;

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {

        ApplicationContext applicationContext = event.getApplicationContext();
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(OrmListener.class);
        map.forEach(new BiConsumer<String, Object>() {

            @Override
            public void accept(String t, Object u) {
                OrmListener ormListener = u.getClass().getDeclaredAnnotation(OrmListener.class);
                Class<?> serviceClass = ormListener.service();
                TableService service;
                if (TableService.class.isAssignableFrom(serviceClass)) {
                    service = (TableService) applicationContext.getBean(ormListener.service());
                } else {
                    service = (TableService) applicationUtils.getBeanLooksLike(serviceClass);
                }
                if (service == null) {
                    return;
                }
                if (u instanceof InsertListener) {
                    service.getMapper().addInsertListener((InsertListener) u);
                } else if (u instanceof DeleteListener) {
                    service.getMapper().addDeleteListener((DeleteListener) u);
                } else if (u instanceof UpdateListener) {
                    service.getMapper().addUpdateListener((UpdateListener) u);
                } else if (u instanceof SelectListener) {
                    service.getMapper().addSelectListener((SelectListener) u);
                }
            }
        });

        Map<TableService<?>, List<TableInitialize>> mapList = new HashMap<>();
        Map<String, TableInitialize> tableInitializeMap = applicationContext.getBeansOfType(TableInitialize.class);
        tableInitializeMap.forEach(new BiConsumer<String, TableInitialize>() {

            @Override
            public void accept(String t, TableInitialize u) {
                Object service = applicationContext.getBean(u.getServiceClass());
                List<TableInitialize> list;
                if (!mapList.containsKey(service)) {
                    list = new ArrayList<>();
                    mapList.put((TableService<?>) service, list);
                } else {
                    list = mapList.get(service);
                }
                list.add((TableInitialize) u);
            }
        });
        mapList.forEach(new BiConsumer<TableService<?>, List<TableInitialize>>() {

            @Override
            public void accept(TableService<?> service, List<TableInitialize> uList) {
                if (service.isEmpty()) {
                    for (int i = 0; i < uList.size(); i++) {
                        uList.get(i).onEmpty(service);
                    }
                }
            }
        });

        if (useCache) {
            applicationContext.getBean(OrmUtils.class).openMapperCacheIgnore(t -> t.toString());
        }
    }

}
